package hhs.scannerapp;

import static java.lang.Math.sin;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.imgproc.Imgproc;

import android.util.Log;

public class EdgeFinder{
	int POINT_LIMIT = 10;
	private List<Point> leftPoints;
	private List<Point> rightPoints;
	private List<Point> depthPoints;
	private Mat mat;
	private Mat mat2;
	private Point[] points;
	final int LIGHT_THRESH = -13413413, DARK_THRESH = 13413413;
	int thresholdType = DARK_THRESH;

	public EdgeFinder(Mat mat, String thresholdType) {
		if(thresholdType.equalsIgnoreCase("light")){
			this.thresholdType = LIGHT_THRESH;
		}
		
		else if(thresholdType.equalsIgnoreCase("dark")){
			this.thresholdType = DARK_THRESH;
		}
		
		this.mat = threshold(mat);
//		Log.e("EdgeFinder", "Mat Size " + mat.rows() + "    "+ mat.cols());
		Runnable leftRunnable = new Runnable()
		{
			public void run() 
			{
//				Log.e("left runnable", "start");
				leftPoints = leftPoints();
//				Log.e("left runnable", "end");
			}
		};
		Thread lThread = new Thread(leftRunnable);
		lThread.start();

		Runnable rightRunnable = new Runnable()
		{
			public void run() 
			{
//				Log.e("right runnable", "start");
				rightPoints = rightPoints();
//				Log.e("right runnable", "end");
			}
		};
		Thread rThread = new Thread(rightRunnable);
		rThread.start();

		try
		{
			lThread.join();
			rThread.join();
		}
		catch(InterruptedException e){}	

		Log.e("tex runnable", "start");
		points = findTexturePoints();
		Log.e("tex runnable", "end");
	}
	
	public EdgeFinder(Mat mat, Mat mat2, String thresholdType) {
		if(thresholdType.equalsIgnoreCase("light")){
			this.thresholdType = LIGHT_THRESH;
		}
		else if(thresholdType.equalsIgnoreCase("dark")){
			this.thresholdType = DARK_THRESH;
		}
		Log.e("EdgeFinder", "Mat Size + Type" + mat.rows() + "    "+ mat.cols()+"    "+mat.type());
		this.mat = threshold(mat);
		this.mat2 = threshold(mat2);
		Log.e("EdgeFinder", "Mat2 Size + Type" + mat2.rows() + "    "+ mat2.cols() + "   " + mat2.type());
		Runnable leftRunnable = new Runnable()
		{
			public void run() 
			{
//				Log.e("left runnable", "start");
				leftPoints = leftPoints();
//				Log.e("left runnable", "end");
			}
		};
		Thread lThread = new Thread(leftRunnable);
		lThread.start();

		Runnable rightRunnable = new Runnable()
		{
			public void run() 
			{
//				Log.e("right runnable", "start");
				rightPoints = rightPoints();
//				Log.e("right runnable", "end");
			}
		};
		Thread rThread = new Thread(rightRunnable);
		rThread.start();
		
		Runnable depthRunnable = new Runnable()
		{
			public void run() 
			{
//				Log.e("right runnable", "start");
				depthPoints = depthPoints();
//				Log.e("right runnable", "end");
			}
		};
		Thread dThread = new Thread(depthRunnable);
		dThread.start();


		try
		{
			lThread.join();
			rThread.join();
			dThread.join();
		}
		catch(InterruptedException e){}	

		Log.e("tex runnable", "start");
		points = findTexturePoints();
		Log.e("tex runnable", "end");
	}

	public Mat threshold(Mat mat){
		int maxValue = 255;
		int minValue = 0;
    	Mat tempMat = new Mat();
    	Imgproc.cvtColor(mat, tempMat, Imgproc.COLOR_BGRA2GRAY);
    	Imgproc.threshold(tempMat, mat, minValue, maxValue, Imgproc.THRESH_OTSU);
    	return mat;
	}

	private List<Point> leftPoints(){
		ArrayList<Point> leftPoints = new ArrayList<Point>();
		int width = mat.width(), height = mat.height();
		for(int i = 0; i < height; i+=10)
		{
			for(int j = 0; j < width; j++){
				if(mat.get(i, j)[0] != 255 && thresholdType == DARK_THRESH){
					leftPoints.add(new Point(j, i));
					j = width;
				}
				else if(mat.get(i, j)[0] != 0 && thresholdType ==LIGHT_THRESH){
					leftPoints.add(new Point(j, i));
					j = width;
				}
			}
		}
		Log.e("OpenCVTest", ""+height+" "+width);

		return leftPoints;
	}

	private List<Point> rightPoints(){
		ArrayList<Point> rightPoints = new ArrayList<Point>();
		int width = mat.width(), height = mat.height();
	
		for(int i = 0; i < height; i+=10)
		{
			for(int j = width-1; j >= 0; j--){
				if(mat.get(i, j)[0] != 255 && thresholdType == DARK_THRESH){
					rightPoints.add(new Point(j, i));
					j = -1;
				}
				else if(mat.get(i, j)[0] != 0 && thresholdType == LIGHT_THRESH){
					rightPoints.add(new Point(j, i));
					j = -1;
				}
			}
		}
		return rightPoints;
	}
	
	private List<Point> depthPoints(){
		ArrayList<Point> depthPoints = new ArrayList<Point>();
		int width = mat2.width(), height = mat2.height();
	
		for(int i = 0; i < height; i+=10)
		{
			for(int j = 0; j < width; j++){
				if(mat2.get(i, j)[0] != 255 && thresholdType == DARK_THRESH){// for point (i,j) check thresholding to to see if it is part of the object
					depthPoints.add(new Point(j, i));
					j = width;
					i = height;
				}
				else if(mat2.get(i, j)[0] != 0 && thresholdType == LIGHT_THRESH){
					depthPoints.add(new Point(j, i));
					j = width;
					i = height;
				}
			}
		}
		
		for(int i = 0; i < height; i+=10)
		{
			for(int j = width-1; j >= 0; j--){
				if(mat2.get(i, j)[0] != 255 && thresholdType == DARK_THRESH){
					depthPoints.add(new Point(j, i));
					j = -1;
					i = height;
				}
				else if(mat2.get(i, j)[0] != 0 && thresholdType == LIGHT_THRESH){
					depthPoints.add(new Point(j, i));
					j = -1;
					i = height;
				}
			}
		}
		return depthPoints;
	}

	/*private boolean isObject(int i, int j) {
		Stack<Point> s = new Stack<Point>();
		ArrayList<Point> found = new ArrayList<Point>();
		s.push(new Point(i,j));
		found.add(new Point(i,j));

		while(!s.isEmpty() && found.size() < POINT_LIMIT) {
			Point base = s.pop();

			int x = (int) base.x;
			int y = (int) base.y;
			if(x+1 != mat.width() &&mat.get(i, j)[0]!=255 && !found.contains(new Point(x+1,y))) {
				s.push(new Point(x+1,y));
				found.add(new Point(x+1,y));
			}
			if(x != 0 && mat.get(i, j)[0]!=255 && !found.contains(new Point(x-1,y))) {
				s.push(new Point(x-1,y));
				found.add(new Point(x-1,y));
			}
			if(y+1 != mat.height() && mat.get(i, j)[0]!=255 && !found.contains(new Point(x,y+1))) {
				s.push(new Point(x,y+1));
				found.add(new Point(x,y+1));
			}
			if(y != 0 && mat.get(i, j)[0]!=255 && !found.contains(new Point(x,y-1))) {
				s.push(new Point(x,y-1));
				found.add(new Point(x,y-1));
			}
		}

		if(s.isEmpty())
			return false;
		return true;
	}*/
	public float[] getCubeValues(){
		float w = (float) (rightPoints.get(0).x-leftPoints.get(0).x);
		float h = (float) (leftPoints.get(leftPoints.size()-1).y-leftPoints.get(0).y);
		float d = (float) (depthPoints.get(depthPoints.size()-1).x-depthPoints.get(0).x);
		return new float[]{w,h,d};
	}
	
	private Point[] findTexturePoints(){
		Point[] points = new Point[2];
		points[0] =leftPoints.get(0);
		points[1] = rightPoints.get(rightPoints.size()-1);
		double radius = (rightPoints.get(0).x - leftPoints.get(0).x)/2;
		double cutLength = sin(7.5)*radius;
		if(cutLength%2==0){
			double centerX = leftPoints.get(0).x + radius;
			points[0].x = centerX-cutLength;
			points[1].x = centerX+cutLength;
		}
		else{
			double centerX = leftPoints.get(0).x + radius+1;
			points[0].x = centerX-cutLength+1;
			points[1].x = centerX+cutLength-1;
		}

		Log.e("find texture points", "finished");
		return points;
	}
	
	public Point[] getCorners(){
		Point [] points = new Point[2];
		double y1 =  leftPoints.get(0).y,  y2=leftPoints.get(leftPoints.size()-1).y;
		double x1 =  leftPoints.get(leftPoints.size()/2).x,  x2=rightPoints.get((rightPoints.size()-1)/2).x;
		points[0] = new Point(x1,y1);
		points[1] = new Point(x2,y2);
		Log.e("Corners", points[0] + " " + points[1]);
		return points;
	}

	public Point[] getTexturePoints(){
		Log.e("get texture points", "finished");
		return points;		
	}

	public List<Point> getLeftPoints() {
		return leftPoints;
	}

	public List<Point> getRightPoints() {
		return rightPoints;
	}



	/*public static List<Point> leftPoints(Bitmap bitmap){
    ArrayList<Point> leftPoints = new ArrayList<Point>();
    int width = bitmap.getWidth(), height = bitmap.getHeight();

    for(int i = 0; i < height; i++)
    {
    for(int j = 0; j < width; j++){
    if(bitmap.getPixel(j, i) != 0){
    leftPoints.add(new Point(j, i));
    j = width;
    }
    }
    }

    return leftPoints;
    }*/

	/*public static List<Point> rightPoints(Bitmap bitmap){
    ArrayList<Point> rightPoints = new ArrayList<Point>();
    int width = bitmap.getWidth(), height = bitmap.getHeight();

    for(int i = 0; i < height; i++)
    {
    for(int j = width-1; j >= 0; j--){
    if(bitmap.getPixel(j, i) != 0){
    rightPoints.add(new Point(j, i));
    j = -1;
    }
    }
    }

    return rightPoints;
    }*/
}
